Skip to content

feat(devframe): proxy-flexible, route-bound WebSocket endpoint#51

Merged
antfu merged 6 commits into
devframes:mainfrom
antfubot:tame-books-hear
Jun 26, 2026
Merged

feat(devframe): proxy-flexible, route-bound WebSocket endpoint#51
antfu merged 6 commits into
devframes:mainfrom
antfubot:tame-books-hear

Conversation

@antfubot

Copy link
Copy Markdown
Collaborator

Why

The dev server already shared one port for HTTP and the RPC WebSocket, but the WS server claimed every upgrade on that port and the SPA discovered it via a server-baked host/port. That breaks two scenarios:

  1. Mixed servers — mounting the socket onto a host server (e.g. a Vite dev server with its own HMR socket and routes) was unsafe, since devframe would intercept unrelated upgrades.
  2. Reverse proxies — when the page is served through a proxy that rewrites the domain, port, or subpath, a host/port baked into __connection.json is no longer reachable.

What

Bind the WebSocket to a dedicated route, shared on the web server's port by default

  • New DEVFRAME_WS_ROUTE (__devframe_ws) constant, mounted next to __connection.json.
  • attachWsRpcTransport gains server, path, and destroyUnmatched options. With a path it routes only the matching upgrade pathname and leaves non-matching upgrades for other listeners (so a host's HMR socket keeps working); it returns a detach(). The server option mounts onto an existing HTTP server, sharing its port.
  • startHttpAndWs gains path and server options — pass an external server to embed the socket inside a host server (devframe never closes a server it doesn't own); when devframe owns the server, off-route upgrades are rejected promptly.

Proxy-flexible __connection.json

  • ConnectionMeta.websocket now accepts number | string | { path?, port?, host? }.
  • The client (resolveWsUrl) follows the page's own origin for the relative/path form — resolving against where __connection.json loaded and only swapping httpws / httpswss — so the connection survives a proxy that changes host/port/subpath. Explicit port/host or a full ws(s):// string opt into a fixed cross-origin endpoint (e.g. a side-car).
  • The standalone dev server now advertises { websocket: { path: "__devframe_ws" } }; the Vite side-car carries its own { port, path } since it is genuinely cross-port.

Backward compatible: with no path, the transport keeps its legacy all-upgrades behavior, and numeric/string websocket descriptors still resolve as before.

Tests

  • resolveWsUrl proxy / cross-origin resolution matrix.
  • Shared-server coexistence with a simulated HMR socket on a sibling route.
  • End-to-end route binding for the dev server (on-route connects, off-route rejected).
  • Full suite green (513 passed); API snapshots updated for the new exports.

This PR was created with the help of an agent.

@netlify

netlify Bot commented Jun 26, 2026

Copy link
Copy Markdown

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit 47ae2b1
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a3dffc111425800088574d3
😎 Deploy Preview https://deploy-preview-51--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@antfu antfu merged commit d0a84e4 into devframes:main Jun 26, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants